package archiving.gui;

import gui.util.FileCheckBox;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.*;

/**
 * This is an extension of the <code>JList</code> class. This list has been
 * modified to provide interface for selecting files. It displays elements as
 * file names with file icon and a checkbox. Selecting the element will select
 * the file.
 * 
 * @author Jakub Závěrka.
 */
class FileCheckList extends JList
{
	private DefaultListModel model;
	//this maps added files to their respective checkboxes
	//private Map<File, JCheckBox> mapping;
	
	/**
	 * Constructor. It initializes the component and the underlying model, as
	 * well as member fields.
	 */
	FileCheckList(){
		model = new DefaultListModel();
		//mapping = new HashMap<File, JCheckBox>();
		setModel(model);
		setCellRenderer(new CheckBoxCellRenderer());
		addMouseListener(new FileCheckListMouseListener());
	}
	
	/**
	 * This will add a file to the list. A new check box (instance of
	 * <code>JCheckBox</code>) is created for the file. The checkbox is then
	 * displayed in the list.<br>
	 * A kind of hack is used to create and paint the checkbox. In order to
	 * display both the file icon and the file name, the checkbox is created
	 * without the text label. A new label (instance of <code>JLabel</code>) is
	 * then created with the label and the file name. Because the
	 * <code>JCheckBox</code> is a child of <code>JComponent</code> and it is a
	 * child of <code>Container</code>, the label is then added to the checkbox.
	 * The label has to be moved right by rougly 15 pixels, or it gets painted
	 * over the checkbox.
	 * 
	 * @param f
	 *           <code>File</code> to be added.
	 * @return A checbox created for the specified file.
	 */
	public FileCheckBox addFile(File f){
		/*JCheckBox checkBox = new JCheckBox();
		
		Icon fileIcon = FileSystemView.getFileSystemView().getSystemIcon(f);
		JLabel content = new JLabel(f.getName(), fileIcon, JLabel.LEFT);
		content.setBorder(BorderFactory.createEmptyBorder(0, 16, 0, 0));
		checkBox.add(content);
		
		model.addElement(checkBox);
		mapping.put(f, checkBox);
		return checkBox;*/
		FileCheckBox checkBox = new FileCheckBox(f);
		model.addElement(checkBox);
		return checkBox;
	}
	
	/**
	 * This will add a new file created form the specified filepath. 
	 * @param f Filepath of the file to be added.
	 * @return JCheckBox created to the corresponding file.
	 */
	public FileCheckBox addFile(String f){
		return addFile(new File(f));
	}
	
	/**
	 * This will populate the list with the file in the specified folder. The
	 * list is cleared first, then for each file in the specified folder the
	 * <code>addFile</code> method is called.
	 * 
	 * @param folder
	 *           A folder to be filled.
	 * @return Mapping of created checkboxes to each of the folder's files.
	 * @throws IllegalArgumentException
	 *            When the provided <code>File</code> is not a directory.
	 */
	public Collection<FileCheckBox> fillFolder(File folder) 
		throws IllegalArgumentException
	{
		if(!folder.isDirectory()){
			throw new IllegalArgumentException("Specified File was not a folder!");
		}
		
		removeAllFiles();
		
		Collection<FileCheckBox> checkboxes = new ArrayList<FileCheckBox>();
		File[] files = folder.listFiles();
		for(int i = 0; i < files.length; i++){
			checkboxes.add(addFile(files[i]));
		}
		return checkboxes;
	}
	
	/**
	 * This will populate the list with files in the folder on the specified
	 * filepath.<br>
	 * For more info, see <code>fillFolder(File folder)</code> method.
	 */
	public Collection<FileCheckBox> fillFolder(String folder){
		return fillFolder(new File(folder));
	}
	
	/**
	 * This removes the specified file from the list and from the underlying mapping.
	 * Reference to the corresponding checkbox is no longer held.   
	 * @param f File to be removed.
	 */
	public void removeFile(File f){
		int size = model.getSize();
		for(int i = 0; i < size; i++){
			FileCheckBox checkbox = (FileCheckBox)model.getElementAt(i);
			if(checkbox.getFile().equals(f)){
				model.remove(i);
				return;
			}
		}
	}
	
	/**
	 * This will remove all files from the list and clear the underlying mapping.
	 * References to all checkboxes are lost.
	 */
	public void removeAllFiles(){
		model.removeAllElements();
	}
	
	/**
	 * This will return the file that is currently in selection.
	 * 
	 * @return File corresponding the the selected item, or <code>null</code>, if
	 *         none of the items is selected.
	 */
	public File getSelectedFile(){
		FileCheckBox checkBox = (FileCheckBox)getSelectedValue();
		return checkBox.getFile();
	}
	
	/**
	 * Mouse listener for the checkboxes in the list. This is needed to implement
	 * the checking of checkboxes when user clicks on an item in the list.
	 *  
	 * @author Jakub Závěrka
	 */
	private class FileCheckListMouseListener extends MouseAdapter
	{
		/**
		 * This method will cause the selected checkbox go the opposite state. 
		 * (Selected if unselected, or unselected if selected).  
		 */
		@Override public void mouseClicked(MouseEvent e){
  			int selectedIndex = locationToIndex(e.getPoint());
			if (selectedIndex < 0) return;
     		
			FileCheckBox item = (FileCheckBox)model.getElementAt(selectedIndex);
     		item.next();
  			repaint();
		}
	}
	
	/**
	 * This is an implementation of <code>ListCellRenderer</code> interface to
	 * support displaying checkboxes with file names and icons in a list.
	 * 
	 * @author Jakub Závěrka
	 */
	private class CheckBoxCellRenderer implements ListCellRenderer
	{
		/**
		 * Because we know that all items in the list are in fact checkboxes, this
		 * will return the checkbox itself, after retyping it from the
		 * <code>Object</code>. This ensures that every checkbox in the list will
		 * get painted correctly. Background will be set to that of the underlying
		 * list, or light blue, if the current component is the selected one.
		 */
		@Override public Component getListCellRendererComponent(JList list,
					Object value, int index, boolean isSelected, 
					boolean cellHasFocus)
		{
			Component val = (Component)value;
			val.setBackground(isSelected ? new Color(127, 127, 255) : list.getBackground());
			return val;
		}
	}
	
	/**
	 * Test method
	 * @param args
	 */
	public static void main(String[] args){
		final JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final FileCheckList list = new FileCheckList();
		list.fillFolder("D:\\Sdílené dokumenty\\Stažené soubory");
		
		f.add(new JScrollPane(list));
		
		JButton add = new JButton("Přidej...");
		add.addActionListener(new ActionListener(){

			@Override public void actionPerformed(ActionEvent e){
				JFileChooser fch = new JFileChooser();
				if(fch.showOpenDialog(f) == JFileChooser.APPROVE_OPTION){
					list.addFile(fch.getSelectedFile());
				}
			}
			
		});
		
		JButton remove = new JButton("Odstraň");
		remove.addActionListener(new ActionListener(){

			@Override public void actionPerformed(ActionEvent e){
				list.removeFile(list.getSelectedFile());
			}
			
		});
		
		JButton removeAll = new JButton("Odstraň vše");
		removeAll.addActionListener(new ActionListener(){

			@Override public void actionPerformed(ActionEvent e){
				list.removeAllFiles();
			}
			
		});
		
		JButton folder = new JButton("Nahraj složku");
		folder.addActionListener(new ActionListener(){

			@Override public void actionPerformed(ActionEvent e){
				JFileChooser fch = new JFileChooser();
				fch.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
				if(fch.showOpenDialog(f) == JFileChooser.APPROVE_OPTION){
					list.fillFolder(fch.getSelectedFile());
				}
			}
			
		});
		
		f.add(add, BorderLayout.SOUTH);
		f.add(remove, BorderLayout.NORTH);
		f.add(removeAll, BorderLayout.EAST);
		f.add(folder, BorderLayout.WEST);
		
		f.pack();
		f.setVisible(true);
	}
}
